/**
 * 
 */
package org.kwisatz.nodistun;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;


class LocalProxyServer extends Thread
{
	static final Logger logger=Logger.getLogger(LocalProxyServer.class.getName());
	private static int debugCounter=0;
	private final class ClosingAction implements Runnable
	{
		private final byte[] passwd;
		private final long sessionId;
		private final OutputStream out;
		private final InputStream in;
		private final byte[] login;
		private PipeStream pipeStream;
		private Socket socket;
		private boolean done=false;

		ClosingAction(long sessionId, InputStream in,
			OutputStream out, byte[] login, byte[] passwd, Socket socket)
		{
			this.passwd = passwd;
			this.sessionId = sessionId;
			this.out = out;
			this.in = in;
			this.login = login;
			this.socket = socket;
		}

		public void run()
		{
			if(!done) synchronized (this)
			{
				try
				{
					socket.close();
					debugCounter++;
					logger.info("Socket closed, trying to reconnect. "+debugCounter);
					Socket remoteSocket=new Socket(remoteHost, remotePort);
					OutputStream remoteOut = remoteSocket.getOutputStream();
					InputStream remoteIn = remoteSocket.getInputStream();
					DataOutputStream dout = new DataOutputStream(remoteOut);								
					remoteOut.write(2);
					remoteOut.write(login.length);
					remoteOut.write(login);
					remoteOut.write(passwd.length);
					remoteOut.write(passwd);
					dout.writeLong(sessionId);
					dout.flush();
					logger.info("Reconnected. "+debugCounter);
					ClosingAction closingAction = new ClosingAction(
						sessionId, in, out, login, passwd, remoteSocket);
					PipeStream inPipe = new PipeStream("LocalProxyServer<-"+debugCounter, remoteIn, out);				
					inPipe.setClosingAction(closingAction);
					inPipe.start();
					
					PipeStream outPipe = new PipeStream("LocalProxyServer->"+debugCounter, in, remoteOut);
					outPipe.setClosingAction(closingAction);
					outPipe.start();
					done=true;
				}
				catch(IOException ex)
				{
					ex.printStackTrace();
				}
			}
		}
	}

	private int port;
	private String remoteHost;
	private int remotePort;
	
	LocalProxyServer(int port, String remoteHost, int remotePort)
	{
		this.port=port;
		this.remoteHost=remoteHost;
		this.remotePort=remotePort;
	}
	
	public void run()
	{
		try
		{
			ServerSocket server=new ServerSocket(port);
			while(true)
			{
				Socket s = server.accept();
				InputStream in=s.getInputStream();
				OutputStream out=s.getOutputStream();
				int version = in.read();
				int nb = in.read();
				out.write(5);
				out.write(2);
				in.read(); // 0x00
				in.read(); // 0x02
				in.read(); // 0x01
				int llogin = in.read();
				byte[] login=new byte[llogin];
				in.read(login);
				int lpasswd = in.read();
				byte[] passwd=new byte[lpasswd];
				in.read(passwd);
				out.write(new byte[] { 1, 0 }); //user valid
				//out.write(new byte[] { 1, 1 }); //user invalid
				in.read(); // 0x05
				in.read(); // 0x01
				in.read(); // 0x00
				in.read(); // 0x01
				byte[] hostBuffer = new byte[6];
				in.read(hostBuffer);
				String ip = hostBuffer[0]+"."+hostBuffer[1]+ '.' +hostBuffer[2]+ '.' +hostBuffer[3];
				int port = 256 * hostBuffer[4] + hostBuffer[5];
				Socket remoteSocket=new Socket(remoteHost, remotePort);
				OutputStream remoteOut = remoteSocket.getOutputStream();
				InputStream remoteIn = remoteSocket.getInputStream();
				remoteOut.write(1);
				remoteOut.write(llogin);
				remoteOut.write(login);
				remoteOut.write(lpasswd);
				remoteOut.write(passwd);
				remoteOut.write(hostBuffer);
				
				DataInputStream din = new DataInputStream(remoteIn);
				long sessionId = din.readLong();
				logger.info("Session ID is "+sessionId);
				out.write(new byte[]{0, 0, 0, 1}); //reply IPV4
				out.write(hostBuffer);
								
				ClosingAction closingAction = new ClosingAction(sessionId, in, out, login, passwd, remoteSocket);
				PipeStream inPipe = new PipeStream("LocalProxyServer<-", remoteIn, out);				
				inPipe.setClosingAction(closingAction);				
				inPipe.start();
				
				PipeStream outPipe = new PipeStream("LocalProxyServer->", in, remoteOut);
				outPipe.setClosingAction(closingAction);
				outPipe.start();
			}
		}
		catch(IOException ex)
		{
			ex.printStackTrace();
		}
	}		
}